#include <dwget.h>

d_wget_t*
d_wget_new()
{
  d_wget_t *new = (d_wget_t *) malloc(sizeof(d_wget_t));

  new->user_agent = LIBDWGET;
  new->url = NULL;
  new->dest = NULL;
  new->hash = d_map_new();
  new->size = 0;
  new->result = NULL;
  new->progress = NULL;
  new->progress_data = NULL;

  return new;
}

void
d_wget_free(d_wget_t **wget)
{
  d_wget_t *obj = *wget;
  if (obj) {
    if (obj->url) free(obj->url);
    if (obj->dest) free(obj->dest);
    if (obj->hash) d_map_free(&obj->hash);
    if (obj->result) d_wget_result_free(&obj->result);
    free(obj);
  }
  *wget = NULL;
}

void
d_wget_result_free(d_wget_result_t **result)
{
  d_wget_result_t *obj = *result;
  
  free(obj);
  *result = NULL;
}

void
d_wget_set_url(d_wget_t *wget, char *url)
{
  wget->url = strdup(url);
}

void
d_wget_set_dest(d_wget_t *wget, char *dest)
{
  wget->dest = strdup(dest);
}

void
d_wget_set_hash(d_wget_t *wget, char *type, char *hash)
{
  d_map_pushback(wget->hash, d_map_data_new(type, hash, NULL));
}

void
d_wget_set_hash_list(d_wget_t *wget, d_map_t *list)
{
  if (wget->hash) d_map_free(&wget->hash);
  wget->hash = list;
}

void
d_wget_set_size(d_wget_t *wget, unsigned ko)
{
  wget->size = ko; 
}

void
d_wget_set_user_agent(d_wget_t *wget, char *user_agent)
{
  wget->user_agent = user_agent;
}

void
d_wget_set_progress_callback(d_wget_t *wget, d_wget_progress_t progress)
{
  wget->progress = progress;
}

void
d_wget_set_progress_data(d_wget_t *wget, void *data)
{
  wget->progress_data = data;
}

d_bool_t
d_wget_check(d_wget_t *wget)
{
  d_error_t *error=NULL;

  d_map_iterator_t *iterator=NULL;
  d_map_data_t *data=NULL;

  struct stat file_stat;

  //file not exist
  if (stat(wget->dest, &file_stat)) {
    goto error;
  }
  
  //file size
  if (wget->size > 0) {
    if (wget->size != (file_stat.st_size / 1024)) {
      wget->result->size = d_false;
      goto error;
    }
    wget->result->size = d_true;    
  }

  //file hash
  if (wget->result->hash) d_hash_free(&wget->result->hash);
  wget->result->hash = d_hash_new(&error);
  if (error) {
    d_error_display(error);
    d_error_free(&error);
  }
  d_hash_add_file(wget->result->hash, wget->dest);
  if (wget->hash) {
    for(iterator=d_map_begin(wget->hash); d_map_iterator_valid(iterator);
	d_map_next(&iterator)) {

      d_map_data(iterator, &data);
      d_hash_add_reference(wget->result->hash, wget->dest, data->key,
                           (char *)data->value);
    }
    d_map_free_iterator(&iterator);
    d_hash_perform_references(wget->result->hash, &error);
    if (error) {
      d_error_display(error);
      d_error_free(&error);
    }
    if (! d_hash_check(wget->result->hash, &error)) goto error;
  }
  return d_true;
 error:
  return d_false;
}

d_bool_t
d_wget_file_exist(d_wget_t *wget)
{
  int ret;
  struct stat file_stat;

  ret = stat(wget->dest, &file_stat);
  if ( ret ) {
    return d_false;
  }
  return d_true;
}

void
d_wget_download(d_wget_t *wget, d_error_t **error)
{
  long int response;
  CURL *curl_handle = NULL;
  FILE *file_handle = NULL;

  if ( ! wget->url ) goto error_args;
  if ( ! wget->dest ) goto error_args;

  curl_handle = curl_easy_init();
  if (curl_handle == NULL) goto error_curl_init;

  file_handle = fopen(wget->dest, "w+");
  if (file_handle == NULL) goto error_open_file;

  curl_easy_setopt(curl_handle, CURLOPT_URL, wget->url);
  curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, wget->user_agent);
  curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, file_handle);
  curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, d_false);

  if (wget->progress) {
    curl_easy_setopt(curl_handle, CURLOPT_PROGRESSFUNCTION, wget->progress);
    if (wget->progress_data) {
      curl_easy_setopt(curl_handle, CURLOPT_PROGRESSDATA, wget->progress_data);
    }
  }

  curl_easy_perform(curl_handle);

  curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response);
  switch(response) {
  case 0:
    d_error(LIBDWGET, "0 protocol unknown");
    goto error;
  case 200:
    goto ok;
  case 404:
    d_error(LIBDWGET, "404 File not found");
    goto error;
  default:
    fprintf(stderr, "Reponse code: %ld\n", response);
    goto ok;
  }

 error_args:
  d_error(LIBDWGET, "set url, dest.");
  return;
 error_curl_init:
  d_error(LIBDWGET, "error curl init.");
  return;
 error_open_file:
  d_error(LIBDWGET, "error openning file in write mode.");
  curl_easy_cleanup(curl_handle);
  return;
 error:
  fclose(file_handle);
  curl_easy_cleanup(curl_handle);
  unlink(wget->dest);
  return;
 ok:
  wget->result->downloaded = d_true;
  fclose(file_handle);
  curl_easy_cleanup(curl_handle);
  return;  
}

void
d_wget_perform(d_wget_t *wget, d_error_t **error)
{
  if (! wget) {
    d_error(LIBDWGET, "wget need to be valid.");
    return;
  }

  wget->result = (d_wget_result_t *) malloc(sizeof(d_wget_result_t));
  wget->result->hash = NULL;

  if (d_wget_file_exist(wget)) {
    if (d_wget_check(wget)) {
      wget->result->downloaded = d_false;
      return;
    }
  }
  
  d_wget_download(wget, error);
  if (! *error) {
    if (! d_wget_check(wget)) {
      d_error(LIBDWGET, "check error.");
    }
  }
}

void
d_wget_result_display(d_wget_t *wget)
{
  d_error_t *error=NULL;

  if (! wget) {
    fprintf(stderr, "wget can't be NULL\n");
    return;
  }
  if (! wget->result) {
    fprintf(stdout, "no result info.\n");
    return;
  }
  fprintf(stdout, "file: %s\n", wget->dest);
  fprintf(stdout, "downloaded: ");
  if (wget->result->downloaded) {
    fprintf(stdout, "yes\n");
  } else {
    fprintf(stdout, "no\n");
  }
  fprintf(stdout, "size: ");
  if (wget->result->size) {
    fprintf(stdout, "ok\n");
  } else {
    fprintf(stdout, "error\n");
  }
  if (d_map_length(wget->hash)) {
    d_hash_display_diff(wget->result->hash);
    if (error) {
      d_error_display(error);
      d_error_free(&error);
    }
  }

}
